// 和用户模块相关的中间件
const bcrypt = require("bcryptjs");

const { getUserInfo } = require("../service/user.service");
const {
  userFormatError,
  userAlreadyExists,
  userRegisterError,
  userDoesNotExists,
  userLoginError,
  invalidPassword,
} = require("../constant/error.type");

/**
 * 用户信息合法性校验中间件
 * @param {*} ctx 请求上下文
 * @param {*} next 放行函数
 * @returns
 */
const userValidator = async (ctx, next) => {
  const { user_name, password } = ctx.request.body;
  // 合法性判断（如：数据格式必须正确）
  if (!user_name || !password) {
    // 打印错误，记录错误日志
    console.error("用户名或密码为空", user_name, password);
    // 触发错误事件，将错误进行统一处理，便于复用
    ctx.app.emit("error", userFormatError, ctx);
    // 返回，不再执行后续代码
    return;
  }
  // 交由下一个中间件处理
  await next();
};

/**
 * 验证用户合理性
 * @param {*} ctx 请求上下文
 * @param {*} next 放行函数
 */
const verifyUser = async (ctx, next) => {
  try {
    const { user_name } = ctx.request.body;
    const res = await getUserInfo({ user_name });
    // 合理性判断（如：若已存在该用户，则不应继续注册该用户）
    if (res) {
      console.error("用户名已经存在", { user_name });
      // 触发错误事件，将错误进行统一处理，便于复用
      ctx.app.emit("error", userAlreadyExists, ctx);
      // 返回，不再执行后续代码
      return;
    }
  } catch (error) {
    // 使用console.error记录错误日志
    console.error("获取用户信息错误", error);
    ctx.app.emit("error", userRegisterError, ctx);
    // 返回，出现错误后，中间件不再往下执行
    return;
  }
  // 交由下一个中间件处理
  await next();
};

/**
 * 密码加密中间件
 * 虽然该加密中间件的加密逻辑代码可以放到后面的register controller进行处理，但这样的话耦合性太高，不利于后面方案的修改和扩展
 * @param {*} ctx 请求上下文
 * @param {*} next 放行函数
 * @returns
 */
const cryptPassword = async (ctx, next) => {
  try {
    const { password } = ctx.request.body;
    // 同步生成hash杂凑次数为10次的盐（10次加盐），次数越高越安全，默认10次
    const salt = bcrypt.genSaltSync(10);
    // 对密码进行加密，hash保存的是密文
    const hash = bcrypt.hashSync(password, salt);
    // 把密文挂载到请求体的password上，即把原来的明文密码覆盖掉
    ctx.request.body.password = hash;
    // 交由下一个中间件处理
    await next();
  } catch (error) {
    // 使用console.error记录错误日志
    console.error("获取用户信息错误", error);
    ctx.app.emit("error", userRegisterError, ctx);
    // 返回，出现错误后，中间件不再往下执行
    return;
  }
};

/**
 * 登录验证
 * @param {*} ctx 请求上下文
 * @param {*} next 放行函数
 * @returns
 */
const verifyLogin = async (ctx, next) => {
  try {
    const { user_name, password } = ctx.request.body;
    // 1.判断用户是否存在（不存在：报错）
    const res = await getUserInfo({ user_name });
    if (!res) {
      console.error("用户名不存在", { user_name });
      // 触发(发送)错误事件，将错误进行统一处理，便于复用
      ctx.app.emit("error", userDoesNotExists, ctx);
      // 返回，不再执行后续代码
      return;
    }
    // 2.密码是否匹配（不匹配：报错）
    /**
     * compareSync会同步比较登录密码和注册密码这两个密码是否相同(匹配)，相同会返回true，不相同会返回false
     */
    if (!bcrypt.compareSync(password, res.password)) {
      ctx.app.emit("error", invalidPassword, ctx);
      // 返回，不再执行后续代码
      return;
    }
  } catch (error) {
    // 使用console.error记录错误日志
    console.error("用户登录错误", error);
    ctx.app.emit("error", userLoginError, ctx);
    // 返回，出现错误后，中间件不再往下执行
    return;
  }
  // 交由下一个中间件处理
  await next();
};

module.exports = {
  userValidator,
  verifyUser,
  cryptPassword,
  verifyLogin,
};
